﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Net;
using System.IO;
using System.Text;

namespace Minecraft_Xenon
{
    public class DownloaderMT
    {
        private string URL;
        private string FilePath;
        private string FileName;
        private int ThreadNumber;
        private int CompletedThreadNumber;
        private Thread[] DownloadThreads;
        private bool UsingTempFile;
        private string TempFolder;
        private List<string> TempFiles;

        private byte[] Buffer = new byte[4096];

        private float TotalPercent;
        private int TotalSpeed;
        private bool Completed;
        private System.Timers.Timer Timer;

        private int TotalFileLength;
        private int TotalFileDownloadedLength;
        private int TotalLastLength;

        /// <summary>
        /// 多线程下载
        /// </summary>
        /// <param name="url"></param>下载地址
        /// <param name="path"></param>存储地址
        /// <param name="filename"></param>文件名
        /// <param name="threadnumber"></param>线程数
        /// <param name="usingTemp"></param>是否使用临时文件下载方式
        /// <param name="tempFolder"></param>临时文件夹位置
        public DownloaderMT(string url, string path, string filename, int threadnumber, bool usingTemp, string tempFolder)
        {
            URL = url;
            FilePath = path;
            FileName = filename;
            ThreadNumber = threadnumber;
            DownloadThreads = new Thread[threadnumber];
            UsingTempFile = usingTemp;
            TempFolder = tempFolder;
            TempFiles = new List<string>();
        }

        /// <summary>
        /// 多线程下载（不使用临时文件，未完成）
        /// </summary>
        /// <param name="url"></param>下载地址
        /// <param name="path"></param>存储地址
        /// <param name="filename"></param>文件名
        /// <param name="threadnumber"></param>线程数
        public DownloaderMT(string url, string path, string filename, int threadnumber)
        {
            URL = url;
            FilePath = path;
            FileName = filename;
            ThreadNumber = threadnumber;
            DownloadThreads = new Thread[threadnumber];
            UsingTempFile = false;
        }

        /// <summary>
        /// 取得下载进度
        /// </summary>
        /// <returns></returns>
        public float GetProgress()
        {
            return TotalPercent;
        }

        /// <summary>
        /// 取得下载速度
        /// </summary>
        /// <returns></returns>
        public int GetSpeed()
        {
            return TotalSpeed;
        }

        /// <summary>
        /// 是否下载完成
        /// </summary>
        /// <returns></returns>
        public bool IsCompleted()
        {
            return Completed;
        }

        private void calculateSpeed(object source, System.Timers.ElapsedEventArgs e)
        {
            TotalSpeed = (TotalFileDownloadedLength - TotalLastLength) / 1024;
            TotalLastLength = TotalFileDownloadedLength;
            TotalPercent = (float) ((float)TotalFileDownloadedLength / (float)TotalFileLength);

            if (CompletedThreadNumber == ThreadNumber && TotalPercent == 1)
            {
                Complete();
            }
        }

        public void Start()
        {
            if (!Directory.Exists(TempFolder))
            {
                Directory.CreateDirectory(TempFolder);
            }
            if (!Directory.Exists(FilePath))
            {
                Directory.CreateDirectory(FilePath);
            }

            WebRequest webRequest = null;
            WebResponse webResponse = null;

            try
            {
                webRequest = WebRequest.Create(URL);
                webResponse = webRequest.GetResponse();

                //取得文件总长度
                TotalFileLength = (int)webResponse.ContentLength;

                //分割文件，平均分配
                int singelLength = (int)(TotalFileLength / ThreadNumber);
                Console.WriteLine("单次长度： " + singelLength);
                int remainLength = (int)(TotalFileLength % ThreadNumber);
                Console.WriteLine("剩余长度： " + remainLength);

                //处理不同的下载方式
                if (!UsingTempFile)
                {
                    //创建空文件
                    FileStream fs = new FileStream(FilePath + FileName, FileMode.Create);
                    fs.Seek((long)TotalFileLength, SeekOrigin.Begin);
                    fs.WriteByte(0);
                    fs.Close();
                }

                //计算速度、进度
                Timer = new System.Timers.Timer(500);
                Timer.Elapsed += new System.Timers.ElapsedEventHandler(calculateSpeed);
                Timer.Enabled = true;

                //创建线程
                for (int i = 0; i < ThreadNumber; i++)
                {
                    List<int> range = new List<int>();
                    range.Add(i * singelLength);

                    if (remainLength != 0 && (ThreadNumber - 1) == i)
                    {
                        range.Add(i * singelLength + singelLength + remainLength);
                        Console.WriteLine("长度分配["+ i + "]： " + (i * singelLength + singelLength + remainLength));
                    }
                    else if (remainLength == 0 && (ThreadNumber - 1) == i)
                    {
                        range.Add(i * singelLength + singelLength);
                        Console.WriteLine("长度分配[" + i + "]： " + (i * singelLength + singelLength));
                    }
                    else
                    {
                        range.Add(i * singelLength + singelLength - 1);
                        Console.WriteLine("长度分配[" + i + "]： " + (i * singelLength + singelLength - 1));
                    }

                    if (UsingTempFile)
                    {
                        string s = TempFolder + FileName + ".[" + i + "].temp";
                        FileStream fs = new FileStream(TempFolder + FileName + ".[" + i + "].temp", FileMode.Create);
                        TempFiles.Add(s);

                        DownloadThreads[i] = new Thread(() =>
                        {
                            //下载文件
                            Download(range[0], range[1], s);
                        });

                    }
                    DownloadThreads[i].Name = "Downloading Thread[" + i + "]";
                    DownloadThreads[i].Start();
                }
            }
            catch (NotImplementedException)
            {
                Console.WriteLine("下载失败");
            }
        }

        /// <summary>
        /// 下载制定开头结尾的文件部分
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="file"></param>
        private void Download(int from, int to, string file)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
                request.Timeout = 10000;
                request.AddRange(from, to);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                if (UsingTempFile)
                {
                    Stream stream = response.GetResponseStream();
                    //向临时文件写入
                    FileStream fs = new FileStream(file, FileMode.Create, FileAccess.Write);

                    //读取、写入
                    int i;

                    while ((i = stream.Read(Buffer, 0, Buffer.Length)) > 0)
                    {
                        fs.Write(Buffer, 0, Buffer.Length);
                        TotalFileDownloadedLength += i;
                    }
                    fs.Close();

                }
                else
                {
                    Stream stream = response.GetResponseStream();
                    //为空文件创建文件流
                    FileStream fs = new FileStream(FilePath + FileName, FileMode.Create, FileAccess.Write);

                    int i;

                    //读取、写入
                    while ((i = stream.Read(Buffer, 0, Buffer.Length)) > 0)
                    {
                        fs.Seek((long)from, SeekOrigin.Current);
                        fs.Write(Buffer, 0, i);
                        TotalFileDownloadedLength += i;
                    }
                    fs.Close();
                }

                CompletedThreadNumber++;
            }
            catch (WebException)
            {
                Download(from, to, file);
                Console.WriteLine("下载失败");
            }

        }

        private void Complete()
        {
            FileStream merge = new FileStream(FilePath + FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            BinaryWriter bw = new BinaryWriter(merge);

            foreach (string file in TempFiles)
            {
                FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);

                bw.Write(br.ReadBytes((int)fs.Length));
                br.Close();
            }

            bw.Close();

            Completed = true;
        }

    }
}
